fix(engine): ignore benign media request aborts#2423
Conversation
vanceingalls
left a comment
There was a problem hiding this comment.
Strengths. Precise classifier: frameCapture.ts:352 gates on exact net::ERR_ABORTED string match — not a fuzzy .includes('abort') — and requires either resourceType === "media" OR a media-extension pathname match. Anything else falls through and is still buffered. Scope is diagnostic-buffer only (appendBrowserDiagnostic) — the underlying request lifecycle, recordScriptLoadFailure at frameCapture.ts:1610, and downstream media-element error events are all untouched. That's the right narrow surface for "suppress the noise, keep the signal."
Handler ordering at frameCapture.ts:1604-1620 correctly runs recordScriptLoadFailure before the ignore gate, so a script failure (regardless of error kind) is still recorded even if the diagnostic append is later gated.
Findings.
important — sibling helper drift. shouldIgnoreRequestFailure already exists at packages/cli/src/commands/validate.ts:76 with the SAME classifier logic (errorText === "net::ERR_ABORTED" AND (resourceType === "media" OR media-extension pathname)), used at validate.ts:458 and packages/cli/src/utils/checkBrowser.ts:224. This PR adds a second implementation with a slightly different signature and — critically — a divergent extension list: the new regex frameCapture.ts:361 adds ogv, but MEDIA_EXTENSIONS at validate.ts:47 does not. Two implementations that were already at risk of drift are now demonstrably drifted on their first landing. @hyperframes/cli already imports @hyperframes/engine (workspace:*), so consolidation is architecturally clean: promote a single shouldIgnoreRequestFailure(url, errorText, resourceType?) from frameCapture.ts (or a shared engine util) and have validate.ts + checkBrowser.ts import it. Follow-up ticket at minimum.
important — .ogv coverage gap in CLI. Concrete manifestation of the drift above: an Ogg Theora media abort will now be silenced on the engine's diagnostic buffer but flagged as Failed to load ... by hf validate and hf check-browser. That's a divergent operator-facing signal for the same underlying event.
nit — missing .ogv regression test. frameCapture.test.ts:225-256 covers media resourceType, .oga extension fallback, non-ABORTED error, and non-media resourceType, but no case pins .ogv — the ONLY extension this PR added over the CLI sibling. Add one assertion so a future accidental removal is caught.
nit — malformed-URL branch untested. The try/catch at frameCapture.ts:363-367 returns false on new URL() failure, which is the correct conservative default, but nothing exercises it. One line of test coverage buys future refactor safety.
Verdict: APPROVE
Reasoning: The fix itself is right-shaped and CI-green (36/36 required checks pass). The classifier is precise, the funnel invariant is preserved (suppression is at the diagnostic-buffer layer, not the load-completion layer), and regression tests cover the branch matrix. The duplication is a real drift bug seed but doesn't gate merge — worth a follow-up to consolidate before the two extension lists diverge further.
— Via
james-russo-rames-d-jusso
left a comment
There was a problem hiding this comment.
Reviewed at b96f0e0d03. Traced the abort-classification path against the frameCapture observability + retry seam.
Verdict framing
Tight, well-scoped fix. Classification is strict — !== exact string match on net::ERR_ABORTED, not a regex, so no shape-matching false positives. The suppression is at the observability layer only (appendBrowserDiagnostic at frameCapture.ts:1612); script fail-fast still fires because recordScriptLoadFailure (line 1608-1610) runs before the ignore check, and scripts never satisfy the media/extension gate anyway (lines 349-363). Ship.
Blockers
(none)
Concerns
See inline for anchored detail.
- 🟡 C1 (low-medium) URL-extension fallback at
frameCapture.ts:355-359over-scopes vs the docstring. The comment claims the case is "media loads Chromium probes", but the code silences ERR_ABORTED for anyresourceType(including"other") whose URL pathname ends in a media extension. A user-codefetch('/asset.mp4')that legitimately aborts, or an XHR/preload fetch cancelled by a scheduling bug, would be silently dropped. Test atframeCapture.test.ts:236-243codifies this behavior — probably Miguel's intent given Chromium's inconsistent tagging of MediaSource-initiated fetches, but code-vs-doc mismatch. - 🟡 C2 (low) Abort event is dropped entirely from
browserConsoleBuffer(the 200-slot circular buffer atframeCapture.ts:225-235that downstream failure diagnostics dump). If a subsequent render failure needs to correlate "the audio never loaded" against a probe abort, the trail is gone. A debug-level breadcrumb (marker in the buffer without surfacing at diagnostic level) would preserve the observability seam without producing noise.
Nits
- N1 Test only exercises the pure classification function at
frameCapture.test.ts:225-256— not therequest.failure()?.errorText ?? "unknown"fallback atframeCapture.ts:1607, nor the wiring path. Real Puppeteer aborts during teardown can returnnullfromrequest.failure()→"unknown"→ NOT ignored → noise diagnostic. Worth one integration test that hits that shape. - N2 Extension list at
frameCapture.ts:357omits.opus,.m4v,.3gp,.mkv. Low priority — theresourceType === "media"branch catches these when Chromium classifies them correctly, but a bare-fetch.opuswould still spam. - N3 Regex is properly anchored (
$) and case-insensitive, non-capturing group — clean.
What I didn't verify
- Whether Chromium's
net::ERR_ABORTEDcan be raised by a network-side failure the user would want to see (my read of Chromiumnet_error_list.h: ERR_ABORTED is caller-initiated, so the filter is safe — but I didn't cross-check Puppeteer's translation layer). - Whether Puppeteer's
resourceType()returns"media"reliably for<video>/<audio>src loads across the Chromium versions we ship —browserManager.tspin not checked. - The retry loop — I confirmed the fix is observability-layer-only (no resolution-path branching), but did not trace the full frame-capture retry state machine to rule out a caller inspecting
browserConsoleBufferforREQUESTFAILEDmarkers to decide retry-vs-abort. - Whether any dashboard / log aggregator counts
[Browser:REQUESTFAILED]markers and would silently see a drop after this ships.
Merge gate
All required checks green per gh pr checks. Nothing merge-blocking. LGTM from my side, COMMENTED.
| if (input.failureText !== "net::ERR_ABORTED") return false; | ||
| if (input.resourceType === "media") return true; | ||
| try { | ||
| return /\.(?:aac|flac|m4a|mp3|mp4|mov|oga|ogg|ogv|wav|webm)$/i.test( |
There was a problem hiding this comment.
🟡 C1: URL-extension fallback over-scopes vs the docstring intent.
The comment says the case is 'media loads Chromium probes', but the code silences ERR_ABORTED for any resourceType (including "other") whose URL pathname ends in a media extension. The test at frameCapture.test.ts:236-243 codifies this — an other-typed request with URL ending in .mp4 gets the ignore treatment.
Hostile counterexample: user-code fetch('/asset.mp4') that legitimately aborts (network stall, scheduling bug), or an XHR / preload fetch cancelled by unrelated code. Both would be silently dropped even though they're not media probes.
Probably Miguel's intent given how inconsistently Chromium tags MediaSource-initiated fetches, but code-vs-comment mismatch. Two options:
- Tighten the code:
resourceType === 'media' || resourceType === 'image'gate on the extension check. - Update the comment to acknowledge the broader scope ('any request with a media file extension, since Chromium mis-tags MediaSource-initiated fetches').
Low-med severity — this is an observability filter, not a correctness gate. — Review by Rames D Jusso
| recordScriptLoadFailure(session, request.url()); | ||
| } | ||
| if (shouldIgnoreRequestFailureDiagnostic({ resourceType, url, failureText })) return; | ||
| appendBrowserDiagnostic( |
There was a problem hiding this comment.
🟡 C2: Abort dropped entirely from browserConsoleBuffer.
Early-return at line 1612 skips the appendBrowserDiagnostic(...) call — but that also skips writing to browserConsoleBuffer (the 200-slot circular buffer at 225-235 that downstream failure diagnostics dump on a real render failure).
Failure scenario: user reports "my render is missing an audio track", ops looks at the dumped browser console buffer, sees no ERR_ABORTED trail because we suppressed it — no visible correlation between the probe abort and the missing track.
Suggest: preserve a debug-level breadcrumb in the buffer (e.g. an appendBrowserDiagnostic(..., { level: 'debug' }) variant that lands in the buffer but doesn't surface at the diagnostic level). Keeps the observability seam intact for post-hoc correlation.
Not blocking — the current behavior is a strict superset of "we no longer noise-spam on probe aborts", which is what the PR promises. — Review by Rames D Jusso
Summary
net::ERR_ABORTEDdiagnostics for media requests cancelled during probe/seek.ogaRoot cause
The capture session appended every
requestfailedevent to its browser diagnostic buffer. Chromium commonly aborts media requests while the probe discovers or seeks audio/video, and producer probe treated any bufferednet::ERR_*line as an asset-load warning even when the final media rendered correctly.Regression coverage
@hyperframes/enginetargeted test: 20/20@hyperframes/enginetypecheckReview
Independent review caught missing
.ogafallback coverage; fixed and re-reviewed with no remaining findings.Reviewer action: verify benign abort suppression does not hide real media/network failures, then approve if CI is green.